package ru.usu.gv.utils;

import ru.usu.gv.core.GraphCanvas;

import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.vecmath.Point2d;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

/**
 @author Sergey Pupyrev 
 15.01.2007
 */

public class GeometryUtil
{
	public static Point3d canvasToVWorldPosition(Point3d center, Point2d p, TransformGroup tg)
	{
		// All transformations are done in VWorld coordinates so set up the
		// transforms necessary to convert
		Transform3D localToVworld = new Transform3D();
		Transform3D imagePlateToVworld = new Transform3D();
		GraphCanvas.getInstance().getImagePlateToVworld(imagePlateToVworld);
		tg.getLocalToVworld(localToVworld);

		// get eye position
		Point3d d = new Point3d();
		GraphCanvas.getInstance().getCenterEyeInImagePlate(d);
		imagePlateToVworld.transform(d);
		Point3d eyePos = new Point3d(d);

		// get mouse position
		GraphCanvas.getInstance().getPixelLocationInImagePlate((int) p.x, (int) p.y, d);
		imagePlateToVworld.transform(d);
		Point3d mousePos = new Point3d(d);

		// calculate vector from eye to canvas
		Vector3d eyeToCanvas = new Vector3d();
		eyeToCanvas.sub(mousePos, eyePos);

		Point3d pos = getVWorldPosition(center, tg);
		// the target position will be at a point in the same plane parallel to
		// the view plate. Calculate the scale factor for eye to canvas in order
		// to place the node from the known z values.
		double t = (pos.z - mousePos.z) / eyeToCanvas.z;

		pos.scale(t, eyeToCanvas);
		pos.add(mousePos);

		// If this method turns out to be too slow we could easily speed it up
		// by calculating the transforms in advance.

		// move the node
		localToVworld.invert();
		localToVworld.transform(pos);
		return pos;
	}

	private static Point3d getVWorldPosition(Point3d center, TransformGroup tg)
	{
		Transform3D localToVworld = new Transform3D();
		tg.getLocalToVworld(localToVworld);
		// get current node position and convert to VWorld
		Point3d pos = new Point3d(center);
		localToVworld.transform(pos);
		return pos;
	}

	public static Point2d vworldToCanvasPosition(Point3d p, TransformGroup tg)
	{
		Transform3D localToVworld = new Transform3D();
		tg.getLocalToVworld(localToVworld);

		Transform3D vWorldToImagePlate = new Transform3D();
		GraphCanvas.getInstance().getVworldToImagePlate(vWorldToImagePlate);
		//		 get current node position and convert to VWorld
		localToVworld.transform(p);
		vWorldToImagePlate.transform(p);

		Point3d d = new Point3d();
		GraphCanvas.getInstance().getCenterEyeInImagePlate(d);
		vWorldToImagePlate.transform(d);

		Point2d result = new Point2d();

		GraphCanvas.getInstance().getPixelLocationFromImagePlate(p, result);
		//System.out.println("Local: " + localToVworld);
		return result;
	}

	public static double calculateAngle(Point3d p0, Point3d p1, Point3d p2)
	{
		double c = p1.distance(p2);
		double a = p0.distance(p1);
		double b = p0.distance(p2);

		double cosa = (a * a + b * b - c * c) / 2.0 / a / b;
		return Math.acos(cosa);
	}

	public static void centralize2(Point3d[] points)
	{
		if (points.length == 0)
			return;

		double mx, Mx, my, My, mz, Mz;
		mx = Mx = points[0].x;
		my = My = points[0].y;
		mz = Mz = points[0].z;
		for (int i = 1; i < points.length; i++)
		{
			Point3d p = points[i];
			mx = Math.min(mx, p.x);
			Mx = Math.max(Mx, p.x);
			my = Math.min(my, p.y);
			My = Math.max(My, p.y);
			mz = Math.min(mz, p.z);
			Mz = Math.max(Mz, p.z);
		}

		double cenx = (mx + Mx) / 2.0;
		double ceny = (my + My) / 2.0;
		double cenz = (mz + Mz) / 2.0;

		for (int i = 0; i < points.length; i++)
		{
			points[i].x -= cenx;
			points[i].y -= ceny;
			points[i].z -= cenz;
		}
	}

	public static void centralize(Point3d[] points)
	{
		if (points.length == 0)
			return;

		Point3d center = new Point3d();
		for (Point3d p : points)
			center.add(p);

		center.scale(1.0 / points.length);

		for (int i = 0; i < points.length; i++)
		{
			points[i].sub(center);
		}
	}
}
